#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<ctype.h>
#include<conio.h>
#include<Windows.h>//SetConsoleCursorPosition 设置控制台光标位置

#define WIDE 60  //宽度
#define HIGH 20  //高度
#define MIN_SLEEP 50.0  // 最小延迟时间，防止速度过快
#define MAX_SLEEP 300.0 // 最大延迟时间

#define BLACK         0  // 黑色
#define BLUE          1  // 蓝色
#define GREEN         2  // 绿色
#define CYAN          3  // 青色，一种介于蓝色和绿色之间的颜色
#define RED           4  // 红色
#define MAGENTA       5  // 品红色，也叫洋红色，常用于四色印刷
#define BROWN         6  // 棕色
#define LIGHTGRAY     7  // 浅灰色
#define DARKGRAY      8  // 深灰色
#define LIGHTBLUE     9  // 浅蓝色
#define LIGHTGREEN   10  // 浅绿色
#define LIGHTCYAN    11  // 浅青色
#define LIGHTRED     12  // 浅红色
#define LIGHTMAGENTA 13  // 浅品红色
#define YELLOW       14  // 黄色
#define WHITE        15  // 白色
//定义蛇对象
struct BODY {
	int X;
	int Y;
};
struct SNAKE {
	struct BODY body[WIDE * HIGH]; //蛇身。body[0]--蛇头
	int size;       //蛇的大小
}snake;

//食物对象
struct FOOD {
	int X;
	int Y;
}food;

double score = 0;//分数
int kx = 0;  //用户按下asdw任意一个按键所得到的坐标值
int ky = 0;  //用户按下asdw任意一个按键所得到的坐标值
int lastX = 0; //描述蛇尾的坐标
int lastY = 0; //描述蛇尾的坐标
double sleepSecond=200; //蛇的移动速度
char lastDirection = 'd'; // 记录上一次移动方向，初始向右
double increase = 5;  //每次加分
double MIN_INCREASE = 1.0; //最低得分
double MAX_INCREASE = 30.0;//最高得分
int choice;  //用户选择
double high_score=0; //最高分
//声明函数
void initSnake(void);
void initFood(void);
void initUI(void);
void playGame(void);
void initWall(void);
void showScore(void);
void explain(void);
void start(void);
void load_file(void);
void show_file(void);
void save_file(void);
void setColor(int color);

int main() {
	start();
	return 0;
}

//初始化蛇
void initSnake(void) {
	snake.size = 2;
	snake.body[0].X = WIDE / 2;
	snake.body[0].Y = HIGH / 2;
	snake.body[1].X = WIDE / 2 - 1;
	snake.body[1].Y = HIGH / 2;
}

//初始化食物
void initFood(void) {
	food.X = rand() % WIDE;//0-59
	food.Y = rand() % HIGH;//0-20
	return;
}

//初始化界面控件(光标函数)
void initUI(void) {
	COORD coord = {0,0};           //光标移动的位置
	//画蛇
	for (int i = 0;i < snake.size;i++) {
		coord.X = snake.body[i].X;
		coord.Y = snake.body[i].Y;
		SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
		setColor(GREEN);
		if (i == 0) putchar('@');
		else putchar('*');  
		setColor(WHITE);
	}
	//去除蛇尾
	coord.X = lastX;
	coord.Y = lastY;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	putchar(' ');
	//画食物
	coord.X = food.X;
	coord.Y = food.Y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	putchar('#');
	coord.X = 0;
	coord.Y = HIGH + 1;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	printf("分数: %.1lf  速度: %.1lf 每次加分：%.1lf", score, 400.0- sleepSecond,increase); // 速度显示转换，值越大表示越快
}

//开始游戏
void playGame(void) {
	char key = 'd';//默认蛇向右移动
	//判断蛇撞墙
	while (snake.body[0].X >= 0 && snake.body[0].X < WIDE && snake.body[0].Y >= 0 && snake.body[0].Y < HIGH) {
		//更新蛇
		initUI();
		//接受用户按键输入ASWD
		if (_kbhit()) {   //为真时说明用户按下按键
			key = _getch();
			// 防止蛇直接往回转向
			if (key == 'w' || key == 's' || key == 'a' || key == 'd') {
				if ((key == 'w' && lastDirection == 's') ||
					(key == 's' && lastDirection == 'w') ||
					(key == 'a' && lastDirection == 'd') ||
					(key == 'd' && lastDirection == 'a')) {
					// 如果用户尝试向相反方向移动，则忽略此次输入
					key = lastDirection;
				}
				else {
					lastDirection = key;
				}
			}
			
		}
		key=tolower(key);
		switch (key) {
		case 'w': kx = 0;ky = -1;break;
		case 's': kx = 0;ky = +1;break;
		case 'd': kx = +1;ky = 0;break;
		case 'a': kx = -1;ky = 0;break;
		case 27:return;//按Esc退出
		case 'q': // 加速
			sleepSecond = sleepSecond * 0.9;
			increase *= 1.25; //速度增加，得分增加
			increase = (double)((int)(increase * 10 + 0.5)) / 10;
			if (increase >=MAX_INCREASE) increase = MAX_INCREASE;
			if (sleepSecond <= MIN_SLEEP) sleepSecond = MIN_SLEEP;
			key = lastDirection;
			break;
		case 'e': // 减速
			sleepSecond =sleepSecond/0.9;
			increase*=0.75; //速度减小，得分减少
			increase = (double)((int)(increase * 10 + 0.5)) / 10;
			if (increase <=MIN_INCREASE) increase = MIN_INCREASE;
			if (sleepSecond >= MAX_SLEEP) sleepSecond = MAX_SLEEP;
			key = lastDirection;
			break;
		default:
			break;
		}
		//蛇头撞身体
		for (int i = 1;i < snake.size;i++) {
			if (snake.body[0].X == snake.body[i].X && snake.body[0].Y == snake.body[i].Y) {
				return;
			}
		}
		//蛇头撞食物
		if (snake.body[0].X == food.X && snake.body[0].Y == food.Y) {
			initFood();//食物消失
			snake.size++;//身体增长
			score=score+increase;//加分
			sleepSecond =sleepSecond*0.9;    //加速
			increase *= 1.25; //速度增加，得分增加
			increase = (double)((int)(increase * 10 + 0.5)) / 10;
			if (increase >= MAX_INCREASE) increase = MAX_INCREASE;
			if (sleepSecond <= MIN_SLEEP) sleepSecond = MIN_SLEEP;
		}
		//存储蛇尾坐标
		lastX = snake.body[snake.size - 1].X;
		lastY = snake.body[snake.size - 1].Y;
		//蛇移动：前一节身体给后一节身体赋值
		for (int i = snake.size-1;i >0;i--) { 
			snake.body[i].X = snake.body[i-1].X;
			snake.body[i].Y = snake.body[i - 1].Y;
		}
		//蛇头坐标根据用户按键赋值
		snake.body[0].X += kx;
		snake.body[0].Y += ky;
		Sleep(sleepSecond);
	}
	return;
}
//画墙
void initWall(void) {
	setColor(RED);
	for (int i = 0;i <= HIGH;i++) {   //多行
		for (int j = 0;j <= WIDE;j++) {  //一行中的多列
			if (j == WIDE) {
				printf("|");
			}
			else if (i == HIGH) {
				printf("_");
			}
			else printf(" ");
		}
		printf("\n");
	}
	setColor(WHITE);
} 
//游戏结束，显示成绩
void showScore(void) {
	//将光标默认位置移动至不干扰游戏的任意位置
	COORD coord;
	coord.X = 0;
	coord.Y = HIGH + 2;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	printf("Game Over!!!\n");
	printf("成绩为：%.1lf\n", score);
	load_file();
	if (score > high_score) {
		setColor(YELLOW);
		printf("恭喜你获得了最高分!");
		high_score = score;
		setColor(WHITE);
		save_file(); //保存最高分
	}
	else {
		setColor(BROWN);
		printf("您没有超过最高分，不要气馁，请继续努力！");
		setColor(WHITE);
	}
	score=0;
	increase=5;
	sleepSecond=200;
	printf("请按任意键返回主菜单");
	while (_kbhit()) _getch();
				_getch();
				system("cls");
				start();
}

//定义游戏说明
void explain(void)
{
	system("cls");
	setColor(BLUE);
	printf("__________________________________________________\n");
	printf("|                                                |\n");
	printf("|   游戏说明：                                   |\n");
	printf("|   分别按下WSAD键来控制贪吃蛇上下左右移动       |\n");
	printf("|   尽可能多地吃到地图中的食物以获得更高分数     |\n");
	printf("|   按下Q键加速将有得分加成                      |\n");
	printf("|   按下E键减速将有得分负加成                    |\n");
	printf("|   吃到食物后蛇将变长并加速                     |\n");
	printf("|   如果蛇撞到地图边界或自身则游戏结束           |\n");
	printf("|                                                |\n");
	printf("|   按任意键返回主菜单                           |\n");
	printf("|________________________________________________|\n");
	setColor(WHITE);
}

void start(void) {
	printf("\n\n");
	printf("                                   --------------------------------------------\n");
	printf("                                   |          欢迎进入贪吃蛇游戏欢迎您        |\n");
	printf("                                   --------------------------------------------\n");
	printf("                                   |             1-----开始游戏               |\n\n");
	printf("                                   |             2-----游戏说明               |\n\n");
	printf("                                   |             3-----查最高分               |\n\n");
	printf("                                   |             4-----退出游戏               |\n\n");
	printf("                                   请选择功能1-4:");
	scanf("%d", &choice);
	if (choice == 1) {
		//设置光标不可见
		CONSOLE_CURSOR_INFO cci;
		cci.dwSize = sizeof(cci);
		cci.bVisible = FALSE;
		SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
		system("cls");
		srand(time(NULL));//播种随机数种子
		initSnake();  //初始化蛇
		initFood();  //初始化食物
		initWall();  //画墙
		initUI();    //画蛇和食物
		playGame();  //开始游戏
		showScore(); //结束游戏，显示分数
	}
	else if (choice == 2) {
		explain();
		printf("请进行下一步操作：");
		while (!_kbhit());
		_getch();
		system("cls");
		start();
	}
	else if (choice == 3) {
		system("cls");
		load_file();
		show_file();
	}
	else if(choice==4){
		system("cls");
		printf("游戏已退出,期待您的下次体验！！\n");
	}
	else{
		system("cls");
		setColor(RED);
		printf("没有该选项，按任意键返回主菜单，并重新输入正确的选项");
		setColor(WHITE);
		while (_kbhit()) _getch();
					_getch();
					system("cls");
					start();
	}
}
//定义加载最高分
void load_file(void)
{ 
	FILE* file=fopen("贪吃蛇成绩.dat", "rb");
	if(file!= NULL){
		fread(&high_score, sizeof(double), 1, file);
		fclose(file);
	}
	else {
		high_score = 0; // 文件不存在或读取失败时设为0
	}

	return;
}

//定义显示最高分
void show_file(void)
{

	system("cls");

	printf("最高分记录: %.1lf 分\n", high_score);
	printf("按下0键清除最高记录，按其他任意键返回主菜单\n");
	while (_kbhit()) _getch();

	if (_getch() == '0')
	{
		printf("确认清除最高记录吗？按下0键清除最高记录，按其他任意键返回主菜单\n");
		if (_getch() == '0')
		{
			high_score = 0;
			save_file();
			printf("成功清除最高记录！按任意键返回主菜单\n");
			while (_kbhit()) _getch();
			_getch();
			system("cls");
			start();
		}
	}
	else {
		system("cls");
		start();
	}
	return;
}

//定义保存最高分
void save_file(void)
{
	FILE* file=fopen("贪吃蛇成绩.dat", "wb");
	if (file!= NULL)
	{
		fwrite(&high_score, sizeof(double), 1, file);
		fclose(file);
	}
	return;
}
//定义颜色
void setColor(int color) {
	HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(hConsole, color);
}